package server;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.util.SortedSet;

/**
 * Creates thread to listen and respond to client spelling queries.
 * 
 * @author the_james_marq
 * @version 26 October 13
 */
public class TCPSpellingServerThread implements Runnable{

	/**
	 * Five seconds.
	 */
	private static final int FIVE_SEC = 5000;
	/**
	 * Word list for queries to be checked against.
	 */
	private WordList word_list;
	/**
	 * Reader from client connection.
	 */
	private BufferedReader my_from_client;
	/**
	 * Stream to client for response.
	 */
	private DataOutputStream my_to_client;
	/**
	 * Socket client is connected on.
	 */
	private Socket client_socket;
	/**
	 * Session id of this query (request address/port).
	 */
	private String session_id;


	/**
	 * Constructor for TCPSpellingServer
	 * 
	 * @param client The client this thread is serving
	 * @throws IOException
	 */
	public TCPSpellingServerThread(Socket client, WordList wlist){
		
		session_id = client.getRemoteSocketAddress().toString();
		Log.out("Enqued connection from " + session_id);
		Log.out(session_id + " - Session starting.");
		
		try {
			my_from_client = new BufferedReader(new InputStreamReader(
					client.getInputStream()));
			my_to_client = new DataOutputStream(
					client.getOutputStream());
		} catch (IOException e) {
			Log.err(session_id + " - Unable to create in/out streams from client.");
		}
				
		client_socket = client;
		word_list = wlist;
		
		try {
			client_socket.setSoTimeout(FIVE_SEC);
		} catch (SocketException e) {
			Log.err(session_id + " - Unable to set timeout.");
		}
	}
	
	/**
	 * Run method to be called when thread is started.
	 */
	public void run() {
		
		String query = null;
		String ret_string = null;

		try {
			while((query = my_from_client.readLine()) != null) {
				ret_string = handleQuery(query);
				my_to_client.writeBytes(ret_string);
				my_to_client.flush();

				if(ret_string.equals("GOODBYE")){break;};
			}
		} catch (SocketTimeoutException e) {
			Log.err(session_id + " - Query time out.");
		} catch (IOException e) {
			Log.err(session_id + " - Error reading/writing streams.");
		}
		try {
			client_socket.close();
		} catch (IOException e) {
			Log.err(session_id + " - Connection closed unexpectadly.");
		}
		Log.out(session_id + " - Connection closed.");
		
	}
	
	/**
	 * Constructs query respones.
	 * 
	 * @param query String to base query on.
	 * @return The string to be passed to the client.
	 */
	private String handleQuery(String query) {
		String[] query_str = query.split(" ");
		if(query_str[0].equals("") && query_str.length < 2){
			return "GOODBYE";
		} else {

			try {
				Integer.parseInt(query_str[0]);
			} catch (NumberFormatException e) {
				Log.err(session_id + " - Malformed packet: non integer index.");
				return "INVALID";
			}


			if(!word_list.isInList(query_str[1])) {
				Log.out(session_id + " - Query word: ("+query_str[1]+")");
				String ret_str = query_str[0] + " NO";
				SortedSet<String> str_set = word_list.getCloseWords(query_str[1]);
				String mesg_str = session_id + " - Word is not spelled correctly, there are " + 
						str_set.size() + " suggestions: ";
				for(String s : str_set) {
					ret_str = ret_str + " " + s;
					mesg_str += " " + s;
				}
				ret_str +="\n";
				if(str_set.size() == 0) {
					Log.out(session_id + " - Word is not spelled correctly. " +
							"There are no suggestions.");
				} else {
					Log.out(mesg_str);
				}
				return ret_str;
			} else if (word_list.isInList(query_str[1])){
				Log.out(session_id + " - Query word: ("+query_str[1]+")");
				Log.out(session_id + " - Word spelled correctly.");
				return query_str[0] + " OK\n";
			} else {
				Log.err(session_id + " - Malformed packet received.");
				return "INVALID";
			}	
		}
	}
	
	public String toString() {
		return client_socket.getRemoteSocketAddress().toString();
	}

}
